dynamic mcp: harden client catalog discovery#772
Conversation
🤖 LLM Evaluation ResultsOpenAI
❌ Failed EvaluationsShow 7 failuresOPENAI1. TestReactEval/[openai]_react_cat_message
2. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
3. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
4. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
5. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
6. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json
7. TestDirectMessageConversations/[openai]_bot_dm_tool_introspection
Anthropic
❌ Failed EvaluationsShow 7 failuresANTHROPIC1. TestReactEval/[anthropic]_react_cat_message
2. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
3. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
4. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
5. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
6. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json
7. TestDirectMessageConversations/[anthropic]_bot_dm_tool_introspection
This comment was automatically generated by the eval CI pipeline. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dbdf599280
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds request-scoped LLM tool runtime state and MCP dynamic tool telemetry; implements MCP tool namespacing/allowlist helpers; refactors tool resolution/store; makes MCP clients, user-clients, and manager context-aware with concurrency-safe caches and slug-based namespacing; and adds tests and retrieval-override metadata. ChangesMCP Dynamic Tool Loading and Telemetry
Tool Store Enhancements and MCP Tool Namespacing
MCP Client Caching, Invalidation, and Tool Discovery
MCP Client Manager and User Client Integration
llmcontext and API plumbing
Tools, web search, prompts, and tests updates
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
mcp/client.go (1)
163-164:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winGuard against nil
pluginAPIbefore session validation.On Line 164,
c.pluginAPIis dereferenced whensessionID != "". If this client is constructed without a plugin API, this path panics instead of returning a typed error.Suggested fix
func (c *EmbeddedServerClient) CreateClient(ctx context.Context, userID, sessionID string) (*Client, error) { // Validate session exists before creating transport (unless empty for tool discovery) if sessionID != "" { + if c.pluginAPI == nil { + return nil, fmt.Errorf("plugin API is required when sessionID is provided") + } mmSession, err := c.pluginAPI.Session.Get(sessionID) if err != nil { return nil, fmt.Errorf("failed to get session: %w", err) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@mcp/client.go` around lines 163 - 164, The code dereferences c.pluginAPI when sessionID != "" (calling c.pluginAPI.Session.Get) which will panic if c.pluginAPI is nil; add a nil check for c.pluginAPI before calling Session.Get and return a typed error (e.g., ErrPluginAPIUnavailable or a wrapped error) when it's nil, then only call c.pluginAPI.Session.Get when c.pluginAPI is non-nil and proceed to validate mmSession as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@llm/tools.go`:
- Around line 665-668: The function toolArgsForLog should guard the argsGetter
before calling it: check if argsGetter == nil and return a safe string like "no
args getter" (or similar) instead of invoking it, then proceed to call
argsGetter(&raw) and handle its error as before; update the toolArgsForLog
function to perform this nil check (referencing the argsGetter parameter and the
toolArgsForLog function) so logging paths that pass a nil getter do not panic.
In `@mcp/client_manager.go`:
- Around line 205-213: The code currently persists transient connect errors by
calling userClient.appendInitialRemoteConnectError(connectErr) after
ConnectToPluginServer fails; instead, make plugin connect errors request-scoped
or cleared on successful connect: remove or avoid appending to the cached client
on transient failure and either collect connectErr in a local slice for this
request (return it with the response) or ensure you clear per-plugin entries on
subsequent successful ConnectToPluginServer calls (e.g., call a
clearInitialRemoteConnectError-like operation after a successful connect).
Update the logic around userClient.ConnectToPluginServer,
userClient.appendInitialRemoteConnectError and
userClient.InitialRemoteConnectErrors so the cached UserClient no longer
permanently stores transient plugin connect failures.
- Around line 184-188: Change GetToolsForUser to accept ctx context.Context as
its first parameter (i.e., func (m *ClientManager) GetToolsForUser(ctx
context.Context, userID string) ...) and remove the context.Background() call;
propagate this ctx into the downstream call to m.getClientForUser(ctx, userID)
and any other internal calls within GetToolsForUser so caller
cancellation/deadlines flow through the LLM entry path.
In `@mcp/user_clients.go`:
- Around line 371-377: The code currently creates a fallback callCtx :=
context.Background() before invoking client.CallToolWithMetadata which allows
tool calls to outlive request cancellation; instead, require a request-scoped
context: remove the context.Background() fallback, validate that llmContext and
llmContext.RequestContext are non-nil (or ensure a context is passed into the
resolver closure) and return an error immediately if missing, then call
client.CallToolWithMetadata using the verified llmContext.RequestContext along
with toolName, args and metadata.
---
Outside diff comments:
In `@mcp/client.go`:
- Around line 163-164: The code dereferences c.pluginAPI when sessionID != ""
(calling c.pluginAPI.Session.Get) which will panic if c.pluginAPI is nil; add a
nil check for c.pluginAPI before calling Session.Get and return a typed error
(e.g., ErrPluginAPIUnavailable or a wrapped error) when it's nil, then only call
c.pluginAPI.Session.Get when c.pluginAPI is non-nil and proceed to validate
mmSession as before.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 2d34a450-b143-49b9-abb0-256fdd5cd29c
📒 Files selected for processing (17)
llm/context.gollm/context_test.gollm/tools.gollm/tools_test.gomcp/client.gomcp/client_embedded_oauth_test.gomcp/client_manager.gomcp/client_manager_filter_test.gomcp/client_manager_test.gomcp/client_test.gomcp/retrieval_overrides.gomcp/tools_cache_test.gomcp/user_clients.gomcp/user_clients_test.gomcpserver/eval_helpers_test.gosearch/search_test.gotelemetry/integration_test.go
dbdf599 to
eb5e85f
Compare
There was a problem hiding this comment.
Stale comment
Security Review: No Actionable Findings
Reviewed the full diff (3014 additions, 424 deletions across 38 files) for this MCP client catalog hardening PR. The changes are security-positive overall.
Key areas examined:
channelAnalysisToolBottool expansion (api/api_channel.go): Overrides the bot's MCP tool allowlist for channel analysis. Initially appeared risky since channel analysis auto-approves all tool calls, but the downstreamAnalyzeChannelfunction correctly scopes the tool set to onlyread_channelandget_channel_infowith boundchannel_idparameters before the LLM sees them. The expanded catalog is only used to locate these two tools.Context propagation:
context.Background()removed from production MCP tool paths.GetToolsForUser,createAndStoreUserClient,ConnectToRemoteServers, andConnectToEmbeddedServerIfAvailablenow accept caller-scopedcontext.Context. Tool resolvers requireRequestContextand fail fast if missing.Tool namespacing and bare-name fallback (
llm/tools.go):lookupToolbare-name resolution correctly returns ambiguous results as not-found.mcpToolAllowedscopes allowlist checks byServerOrigin, preventing cross-server tool confusion.Concurrency safety: New mutex protection (
toolsMu,clientsMu,discoveryMu,notifyOwnerMu) onClientandUserClientsprevents race conditions during tool list invalidation and rediscovery.Plugin MCP client (
mcp/client.go):NewPluginClientcorrectly injects authenticated user ID viaheaderTransportfrom session-derived data, not user input. Tool list changed handlers properly invalidate caches.Trace logging (
llm/tools.go):TraceResolvedlogs full tool results whendoTraceis true, but this flag is not enabled in any production code path.No medium, high, or critical vulnerabilities identified.
Sent by Cursor Automation: Find vulnerabilities
6986e50 to
99205c9
Compare
There was a problem hiding this comment.
Stale comment
Security Review: No Actionable Findings
Reviewed the full diff for this MCP client catalog hardening PR. The changes are security-positive overall.
Key areas examined:
channelAnalysisToolBottool expansion (api/api_channel.go): Overrides the bot's MCP tool allowlist (AutoEnableNewMCPTools = true,EnabledMCPTools = nil). Confirmed safe:channels/channels.go:95-111immediately scopes to a newscopedToolsstore containing onlyread_channelandget_channel_infowith boundchannel_idparameters before the LLM executes.
lookupToolbare-name fallback (llm/tools.go): When the LLM calls a tool by bare name, the store searches all namespaced tools. Correctly returns not-found when ambiguous (multiple servers share the same bare tool name). The resolver always executes within the requesting user's permission scope.
ToolPolicyLookupNameand policy bypass (mcp/tool_policy.go): The productionToolPolicyFuncwrapsLookupToolPolicy, which callsToolPolicyLookupNameto strip namespace prefixes before matching admin-configured policies. Namespaced runtime names (jira__get_issue) correctly match bare policies (get_issue). No bypass path exists.Context propagation:
context.Background()/context.TODO()removed from MCP tool paths. Tool resolvers now requireRequestContextand fail fast with"missing request context for MCP tool call"if nil. This is a security improvement.Plugin MCP client (
mcp/client.go:NewPluginClient):MMUserIDHeaderis injected fromc.userID(sourced from the authenticated session, not user input). ThesourcePluginAPInil check is preserved.Concurrency safety: New mutex protection (
toolsMu,clientsMu,discoveryMu) prevents race conditions duringToolListChangednotifications and rediscovery.createAndStoreUserClientuses a proper double-check-after-lock pattern.Trace logging (
llm/tools.go):TraceResolvedlogs full tool results, but only whendoTraceis true. The productionNewToolStore()call (atllmcontext/llm_context.go:191) passes no options, leavingdoTraceas false.No medium, high, or critical vulnerabilities identified.
Sent by Cursor Automation: Find vulnerabilities
99205c9 to
e9e6f79
Compare
There was a problem hiding this comment.
Stale comment
Security Review — No Findings
Reviewed the full diff (2840 additions across 36 files) for vulnerabilities in channel data isolation, user identity handling, tool/MCP boundaries, prompt injection surface, file access control, search scoping, and secrets exposure.
Security-positive changes in this PR:
- Request context propagation:
GetToolsForUserand tool resolvers now requirecontext.Contextfrom the authenticated request, replacing priorcontext.Background()/context.TODO()calls. This ensures MCP tool calls respect request-scoped cancellation and cannot outlive the originating session.- Tool namespacing: MCP tools are now namespaced by server slug (e.g.,
jira__searchinstead ofsearch), eliminating cross-server tool name collisions. ThelookupToolbare-name fallback correctly rejects ambiguous matches.- Concurrency safety:
Client.toolsaccess is now mutex-protected, andUserClientsoperations use proper RLock/Lock patterns with snapshot-then-release to avoid holding locks during network calls.- Tool resolver hardening: The resolver now fails fast with
"missing request context for MCP tool call"whenRequestContextis nil, enforcing the coding guideline againstcontext.Background()in production paths.- Request-scoped error handling: Plugin connection errors are no longer persisted on the cached
UserClients, preventing transient failures from leaking across requests.Areas examined with no issues found:
channelAnalysisToolBot: Overrides the bot'sAutoEnableNewMCPTools/EnabledMCPToolsto load the full MCP catalog. While this expands available tools beyond the bot's configured allowlist, the requesting user's identity and permissions still scope all tool execution viaRequestContext. The admin-levelfilterToolsByConfigdisabled-tool filtering still applies. This is a policy choice, not a permission bypass.NewPluginClient: User identity is correctly baked into the HTTP transport viaMMUserIDHeaderfrom the session-authenticateduserID, not from user-supplied input.ToolPolicyLookupName/mcpToolAllowedbare-name fallbacks: Always scoped byServerOrigin, so cross-server policy confusion is not possible.- No hardcoded credentials, secrets in logs, or path traversal vectors introduced.
Sent by Cursor Automation: Find vulnerabilities
| // MCPDynamicToolLoading indicates this context uses strict MCP dynamic loading. | ||
| MCPDynamicToolLoading bool | ||
| // MCPDynamicToolTelemetry receives low-cardinality dynamic MCP tool events. | ||
| MCPDynamicToolTelemetry MCPDynamicToolTelemetry | ||
| MCPDynamicToolSearchUsed bool | ||
| MCPDynamicLoadedToolNames map[string]bool | ||
| MCPDynamicSearchLoadCallSuccessRecorded map[string]bool | ||
|
|
||
| // DisabledMCPServerOrigins contains per-user disabled MCP server origins that | ||
| // must be removed before strict registry construction. | ||
| DisabledMCPServerOrigins []string | ||
|
|
||
| // KeepMCPTool, when non-nil, is applied to MCP tools before strict registry | ||
| // construction and before flag-off visible MCP insertion. | ||
| KeepMCPTool func(Tool) bool | ||
|
|
||
| // PreloadedMCPTools contains exact-or-bare MCP tool selectors for internal | ||
| // predefined flows. They are selected only from the already-authorized MCP | ||
| // catalog and are request scoped. | ||
| PreloadedMCPTools []EnabledMCPTool | ||
|
|
||
| // MCPToolRegistry holds the strict MCP tool registry that was built | ||
| // alongside Tools, when MCP dynamic tool loading is enabled. It is stashed | ||
| // here so callers can replay loaded-tool restoration after the conversation | ||
| // row exists without rebuilding the entire tool store. | ||
| // | ||
| // Stored as `any` to avoid an llm -> mcp import cycle: the mcp package | ||
| // already imports llm, and the only consumer that needs the concrete type | ||
| // is the llmcontext package, which can import both. Type-assert to | ||
| // *mcp.ToolRegistry there. | ||
| MCPToolRegistry any |
There was a problem hiding this comment.
These don't belong here. This struct is for prompt building not passing stuff like this.
There was a problem hiding this comment.
I cleaned this up by moving the dynamic MCP/tool execution fields into a nested ToolRuntimeContext on llm.Context, so they’re no longer loose prompt-building fields.
I think this should stay reachable from llm.Context because Context is already the per-turn carrier used by tool construction and execution (Tools already lives there), not just rendered prompt text. The dynamic MCP state needs to be available across the same flow: building the tool store, resolving meta-tools, recording per-turn load/search telemetry, and restoring loaded tools. Splitting it into a separate top-level parameter would force the same runtime object through all resolver/toolrunner/context-builder paths without changing the ownership model. If you're passing it all around to the same places alongside the Context it's looking like a shared tool context anyways.
There was a problem hiding this comment.
Ok makes sense. I think this could use a refactor to make that clear, but out of scope here.
|
|
||
| // RequestContext carries the caller's request-scoped context for downstream | ||
| // work such as MCP tool discovery. May be nil in tests. | ||
| RequestContext stdcontext.Context |
There was a problem hiding this comment.
Doesn't belong here, we should update to pass context as first parameter if needed.
There was a problem hiding this comment.
You thumbs upped but it is still there.
There was a problem hiding this comment.
Should be good now. Did refactor the first time but missed a few spots
| // already imports llm, and the only consumer that needs the concrete type | ||
| // is the llmcontext package, which can import both. Type-assert to | ||
| // *mcp.ToolRegistry there. | ||
| MCPToolRegistry any |
There was a problem hiding this comment.
Specifically for this one I don't think we want any
| if client != nil { | ||
| candidates = append(candidates, client.config.Name) | ||
| } | ||
| candidates = append(candidates, serverID) |
There was a problem hiding this comment.
Not sure the opeque serverID makes sense over the base URL. These are LLM visible so ideally they are not something strange.
There is also the reported name from the MCP server we could use.
There was a problem hiding this comment.
Remote servers: require/configure a name through system console; serverID is that name anyway.
Plugin MCP with a configured name: client.config.Name is the plugin-provided name, so that wins.
Plugin MCP without a configured name: client.config.Name is empty, then fallback uses serverID, which is plugin://.
For that fallback case, serverID and BaseURL are basically the same synthetic origin, so there isn't much of a difference in ordering. I preserved both fallbacks more for safety than anything.
| func (s *ToolStore) AddTools(tools []Tool) { | ||
| for _, tool := range tools { | ||
| s.tools[tool.Name] = tool | ||
| if s.unloadedMCPTools != nil { |
| tools map[string]Tool | ||
| authErrors []ToolAuthError | ||
| tools map[string]Tool | ||
| unloadedMCPTools map[string]ToolInfo |
There was a problem hiding this comment.
Seems more complicated than nessisary? Why have toolinfo? Also why is it unloaded MCP tools and not what is loaded?
There was a problem hiding this comment.
unloadedMCPTools are tools we know to exist in the catalog (ie, from a search) but haven't been loaded yet. ToolInfo is a lightweight struct to reflect what we actually know about it at this point. I've added a comment to clarify
| toolsDirty bool | ||
| toolsGeneration uint64 | ||
| notifyOwnerMu sync.RWMutex | ||
| notifyOwner *Client |
There was a problem hiding this comment.
Feels like we should find a better solution for this.
| }, | ||
| &mcp.ClientOptions{}, | ||
| &mcp.ClientOptions{ | ||
| ToolListChangedHandler: func(ctx context.Context, _ *mcp.ToolListChangedRequest) { |
There was a problem hiding this comment.
Is this for supporting the MCP feature where an MCP server can add tools while holding the connection open? This feels orthogonal to what this PR is trying to accomplish. Maybe a separate PR? Not sure it's even worth supporting.
There was a problem hiding this comment.
This was in the original spec
But I think we can kick support for this down the road now that we have #732 opened, at least end users will be able to refresh.
Removed in next commit
There was a problem hiding this comment.
Stale comment
Security Review — No Findings
Reviewed the full diff (2564 additions, 474 deletions across 43 files) for vulnerabilities in channel data isolation, user identity handling, tool/MCP boundaries, prompt injection surface, file access control, search scoping, and secrets exposure. CI checks pass (31/32 passed, 1 skipped).
Security-positive changes in this PR:
- Request context propagation:
GetToolsForUser,ConnectToRemoteServers,ConnectToEmbeddedServerIfAvailable, and tool resolvers now acceptcontext.Contextfrom the authenticated request, replacingcontext.Background()/context.TODO()calls. MCP tool calls now respect request-scoped cancellation.- Tool namespacing: MCP tools are namespaced by server slug (e.g.,
jira__search), preventing cross-server tool name collisions.lookupToolbare-name fallback correctly rejects ambiguous matches (multiple servers sharing the same bare name).- Concurrency safety:
Client.toolsandUserClients.clientsaccess is now mutex-protected with proper RLock/Lock patterns.Tools()returnsmaps.Clone()to prevent concurrent modification.- Request-scoped error handling: Plugin connection errors are no longer persisted on the cached
UserClients(cloneMCPErrors+ localappendMCPError), preventing transient failures from leaking across requests.Areas examined with no issues found:
channelAnalysisToolBot(api/api_channel.go:179): Overrides the bot'sAutoEnableNewMCPTools/EnabledMCPToolsto load the full MCP catalog for tool discovery. Confirmed safe:channels/channels.go:96-111immediately scopes to a newscopedToolsstore containing onlyread_channelandget_channel_infowith boundchannel_idparameters before the LLM executes. No other tools reach the ToolRunner.NewPluginClient(mcp/client.go): User identity is injected viaMMUserIDHeaderfrom the session-authenticateduserID(not user-supplied input).sourcePluginAPInil check is preserved.ToolPolicyLookupName/mcpToolAllowed: Bare-name fallbacks are always scoped byServerOrigin, preventing cross-server policy confusion.LookupToolPolicydispatches byserverBaseURLbefore stripping namespaces.lookupTool/lookupUnloadedMCPTool: Both iterate all tools for bare-name matches but return not-found on ambiguity (multiple matches), preventing unintended tool resolution.- No hardcoded credentials, secrets in logs/responses, path traversal vectors, or SSRF vectors introduced.
Sent by Cursor Automation: Find vulnerabilities
crspeller
left a comment
There was a problem hiding this comment.
Just need to remove that context from the struct and I think this is good.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
c60dec5 to
272ed61
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Security Review — No Findings
Reviewed the full diff (2564 additions, 474 deletions across 46 files) for vulnerabilities in channel data isolation, user identity handling, tool/MCP boundaries, prompt injection surface, file access control, search scoping, and secrets exposure. CI checks pass (33/34, 1 skipped).
Security-positive changes in this PR:
- Request context propagation:
GetToolsForUser,ConnectToRemoteServers,ConnectToEmbeddedServerIfAvailable, and tool resolvers now acceptcontext.Contextfrom the authenticated request. TheToolResolversignature itself was updated tofunc(ctx context.Context, llmCtx *Context, argsGetter ToolArgumentGetter), eliminatingcontext.Background()shortcuts across production tool-call paths. MCP tool calls now respect request-scoped cancellation and timeouts. - Concurrency safety:
Client.toolsaccess is now protected bytoolsMu(RWMutex), andUserClients.clientsbyclientsMu.Tools()returnsmaps.Clone()to prevent concurrent map modification.ConnectToEmbeddedServerIfAvailableandConnectToPluginServeruse double-check-after-lock patterns to avoid duplicate connections. - Request-scoped error handling:
cloneMCPErrors+ localappendMCPErrorensure plugin connection errors are request-scoped and no longer persisted on the cachedUserClients, preventing transient failures from leaking across requests. - Tool namespacing: MCP tools are namespaced by server slug (e.g.,
jira__search), preventing cross-server tool name collisions.lookupToolbare-name fallback correctly rejects ambiguous matches (multiple servers sharing the same bare name return not-found).
Areas examined with no issues found:
channelAnalysisToolBot(api/api_channel.go:178): Creates a modified bot withAutoEnableNewMCPTools=trueandEnabledMCPTools=nilto expand MCP catalog discovery. Confirmed safe:channels/channels.go:96-111immediately replaces the tool store with ascopedToolsstore containing onlyread_channelandget_channel_infowith boundchannel_idparameters before the LLM executes. No other tools reach the ToolRunner.lookupToolbare-name fallback (llm/tools.go:360-383): When the LLM calls a tool by bare name, the store searches all namespaced tools. Correctly returns not-found when ambiguous (multiple MCP servers share the same bare tool name). Built-in tools (emptyServerOrigin) are excluded from bare-name matching. The resolver always executes within the requesting user's permission scope.ToolPolicyLookupName/LookupToolPolicy(mcp/tool_policy.go): Runtime namespaced names (e.g.,jira__get_issue) are correctly stripped to bare names before matching admin-configured policies. An exact configured name still wins over bare-name fallback. Policy lookups are always scoped byserverBaseURL, preventing cross-server policy confusion.shouldAutoExecuteTool/botChannelAutoEverywhereKeepTool: Both pass namespaced runtime names through theToolPolicyChecker, which dispatches toLookupToolPolicy→ToolPolicyLookupName. Namespace stripping ensures namespaced tools correctly match their configured auto-run/auto-run-everywhere policies.NewPluginClient(mcp/client.go:311-388): User identity is injected viaMMUserIDHeaderfromuserID(sourced from the authenticated session, not user-supplied input).sourcePluginAPInil check is the first guard.ToolRuntimeContext(llm/context.go): New runtime fields (KeepMCPTool,PreloadedMCPTools,restoreMCPDynamicTools, telemetry hooks) are scaffolding for dynamic loading in later stack PRs. All fields are set internally during context building, never from user input.- No hardcoded credentials, secrets in logs/responses, path traversal vectors, or SSRF vectors introduced.
Sent by Cursor Automation: Find vulnerabilities


Summary
Hardens MCP client catalog discovery with safer tool identity handling (duplicate registrations are no longer silently dropped), plugin/embedded discovery refresh behavior, user-client synchronization, and retrieval override plumbing, with backwards-compatible tool policy lookup for configs without namespace plumbing. Also adds bare MCP tool name resolution, LLM context scaffolding for dynamic loading (request context, registry stash, telemetry hooks, preloaded tools), threads request context through conversation and API entry points, fixes channel analysis tool discoverability under narrow allowlists, and removes legacy log-based tool-trace plumbing superseded by OpenTelemetry.
Stack: 2/6
Ticket Link
N/A
Screenshots
N/A
Release Note
Made with Cursor
Summary by CodeRabbit
New Features
Improvements
Tests